/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Definition for common datatype 'Text'
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: text.hpp,v 1.7 2009-05-13 07:18:19 amaula Exp $
 *
 */
#ifndef _MACI_COMMON_TEXT_HPP_
#define _MACI_COMMON_TEXT_HPP_
#include <string>

namespace MaCI {
  namespace Common {

    /** Text.
     * 
     * This structure defines a MaCI common format for transferring
     * dynamic length text. All MaCI interfaces requiring dynamic
     * length text should use this - directly or typedeffed to avoid
     * writing same piece of code all over again.
     *
     * As the structure is dynamic - which is usually not the case
     * with MaCI containers, it has some helper functions
     * available. static functions of the class are intended for
     * automatic construction of TText elements from ordinary
     * std::string compatible dataelements. 
     *
     * To fully take advantage of this common datatype, please use the
     * provided static/nonstatic methods for handling the structure
     * contents. If you feel there should be some other method which
     * adds some value to handling the information, please notify the
     * module author. (see line 4 of this file)
     */
    struct TText 
    {
    public:
      /** Default constructor.
       */
      TText() 
        : length(0),
          text() {}

      /** Create a TText container dynamically.
       *
       * This function creates a new text container. The function is
       * able to allocate the memory by itself, hence the double
       * pointer.
       *
       * @param[out] aMemPtrPtr   Pointer to pointer, which is modified to point to 
       *                          dynamically allocated memory area, containing just
       *                          the data required to fit in the header and text 
       *                          contents.
       * @param[out] aLength      Reference to variable where the final total 
       *                          container size will be stored. Original value will
       *                          be ignored (and overwritten)
       * @param[in] aText         Input text to store.
       * @return                  Pointer to TText header (== pointer to beginning of
       *                          the allocated memory area), or NULL on error.
       */
      static TText *CreateTextElement(uint8_t **aMemPtrPtr, unsigned int &aLength, const std::string &aText) {
        const unsigned int len = sizeof(TText) + aText.size() + 1;
        uint8_t *memblock = new uint8_t[ len ];
        TText *hdr = reinterpret_cast<TText*>(memblock);
        if (hdr) {
          // Datacopy.
          hdr->length = aText.size() + 1;
          memcpy(hdr->text, aText.c_str(), aText.size() + 1);

          // Copy to caller.
          aLength = len;
          *aMemPtrPtr = memblock;
        }
      
        return hdr;
      }
    
    
      /** Create a TText container to user supplied buffer.
       *
       * This function creates a TText container to user supplied
       * buffer. The Length parameter is used for two purposes; first,
       * to define the maximum size of given buffer, and second; to
       * report back to caller the real used space from the provided
       * memory pointer.
       *
       * @param[in]               Pointer to memory location where the constructed
       *                          container will be stored.
       * @param aLength           When function is called, this should reference to
       *                          variable indicating the maximum reserved
       *                          size of the memory are pointed by \p aMemPtr.
       *                          After a succesfull call, this variable will
       *                          contain to total used size from the given memory
       *                          space.
       * @param[in] aText         Input text to store.
       * @return                  Pointer to TText header (== pointer to beginning of
       *                          the memory area), or NULL on error.
       */
      static TText *CreateTextElement(uint8_t *aMemPtr, unsigned int &aLength, const std::string &aText) {
        const unsigned int len = sizeof(TText) + aText.size();
        TText *hdr = reinterpret_cast<TText*>(aMemPtr);
        if (hdr && aLength >= len) {
          // Datacopy.
          hdr->length = aText.size() + 1;
          memcpy(hdr->text, aText.c_str(), aText.size() + 1);
        
          // Copy to caller.
          aLength = len;

        } else {
          // Error in size probably (or NULL memory pointer)
          hdr = NULL;

        }
      
        return hdr;
      }
    

      /** Destroy the memory area created dynamically.
       *
       * This function destroys the dynamically allocated memory area.
       *
       * @param[in] aElement      Pointer to TText element as returned by a
       *                          previous call to 
       *                          'CreateTextElement(uint8_t **aMemPtrPtr, 
       *                                             unsigned int &aLength, 
       *                                             const std::string &aText)'
       *
       */
      static void DestroyTextElement(TText *aElement) {
        delete[] reinterpret_cast<uint8_t *>(aElement);
      }


      /** Destroy the memory area created dynamically.
       *
       * This function destroys the dynamically allocated memory area.
       *
       * @param[in] aElementMemoryPtr Pointer to memory region as 
       *            stored in value of parameter 'aMemPtrPtr' of function
       *            'CreateTextElement(uint8_t **aMemPtrPtr, 
       *                                             unsigned int &aLength, 
       *                                             const std::string &aText)'
       */
      static void DestroyTextElement(uint8_t *aElementMemoryPtr) {
        delete[] aElementMemoryPtr;
      }


      /** Return the total size of the container.
       *
       * This function returns the total container size. This value
       * can be used when the whole container needs to be copied or
       * transferred somewhere.
       *
       * @return                Total size of the container in bytes.
       *
       */
      unsigned int GetContainerSize(void) const {
        return this->length + sizeof(TText);
      }


      /** Get text length.
       *
       * This function returns the length of the text stored in
       * pointer 'text'.
       *
       * @return                Number of bytes in 'text' container.
       */
      uint32_t GetLength(void) const {
        return this->length;
      }


      /** Return constant char pointer to text inside.
       *
       * This function returns a direct pointer to character data
       * inside.
       *
       * @return                Pointer to text inside the container.
       */
      const char *GetTextPtr(void) const {
        return this->text;
      }


      /** Return contents as std::string container.
       *
       * This function converts the contents to std::string container
       * and returns it.
       * 
       * @return                Element data in std::string container.
       */
      std::string GetText(void) const {
        return std::string(this->text, this->length);
      }

      
      /** Print container contents.
       *
       * This function prints container contents through the dPrint
       * facility.
       *
       * @param[in] aLength     Level to print on.
       */
      void Print(const unsigned int aLevel) const {
        dPrint(aLevel,
               "TText: '%s' (%u bytes)",
               text, length);
      }

    public:
      uint32_t length;          ///< Length of text inside container.
      char text[];              ///< Text data in container.
    };
  }
}

#endif
